Skip to content

Initial Studio version - #5

Open
philbir wants to merge 22 commits into
mainfrom
dreamr-wedding
Open

Initial Studio version#5
philbir wants to merge 22 commits into
mainfrom
dreamr-wedding

Conversation

@philbir

@philbir philbir commented Jun 7, 2026

Copy link
Copy Markdown
Owner

No description provided.

philbir and others added 22 commits June 7, 2026 19:06
…lity

- Implemented IAiProvider interface for AI model interaction.
- Created AiEndpoints for managing AI configurations, detecting CLI tools, and handling chat requests.
- Developed DocsEditor component for editing markdown documentation with rich text and preview modes.
- Introduced AssistantPane for user interaction with the AI assistant, including model selection and conversation history.
- Added Markdown component for rendering assistant replies in GitHub-flavored markdown.
- Implemented DuplicateRequestDialog for duplicating requests with new names and paths.
- Styled markdown output with scoped CSS for consistent theming.
…uto-update

Restructure the repo under src/ for a clean, role-based layout and finish the
Tap Studio desktop app's auto-update pipeline (modeled on mango/desktop).

Layout
- src/backend/       the six .NET projects (were src/Tap.*)
- src/ui-inspector/  inspector UI (was ui/)
- src/ui-studio/     Studio workbench UI (was ui-studio/)
- src/desktop/       Tauri desktop shell (was apps/tap-studio-desktop/)
- samples/ stays at root

Re-pointed every reference: Tap.slnx, the BuildTapUi/BuildStudioUi MSBuild
targets (../../ui-inspector, ../../ui-studio), sample AppHost project refs +
Studio.AppHost's runtime Vite dir, Dockerfile, .gitignore, scripts/*, the CI
workflows (desktop/docker/release), the desktop compile-server/sync-version
scripts, and the docs (CLAUDE/AGENTS/README/mantine skill/plan). Verified: the
solution restores at the new paths and both UI-build targets repopulate wwwroot.

Desktop auto-update (mango parity)
- tauri.conf.json: createUpdaterArtifacts + real minisign pubkey.
- lib.rs: check_for_update / install_update commands (wrap the updater plugin,
  app.restart() after install), registered via invoke_handler.
- build.rs: register those commands with the ACL so the sidecar's http origin
  (a remote context) can call them.
- capabilities/default.json: remote.urls allowlist + updater/app-command perms.
- ui-studio: useDesktopUpdater() hook + desktopUpdater.tsx show a Mantine
  "update available" notification with an install-and-restart button; no-op
  outside the desktop shell. Adds @tauri-apps/api.

Workspace store hardening
- KnownWorkspaceStore prunes workspaces whose folder no longer exists on boot,
  so a stale pointer (e.g. an old project path left behind by a reorg) can't be
  selected and 500 on every read. Verified end-to-end: a seeded stale/active
  path self-heals and GET /api/auths/ returns 200.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Spectre.Console.Cli's version line lags the core package (no 0.56/0.57 release),
so the central pin is 0.55.0. That release changed the command base classes:
Execute/ExecuteAsync overrides are now `protected` and take a trailing
CancellationToken. Update all six commands to match.

The long-running commands (run, ui) keep their own PosixSignal/CancelKeyPress
cancellation source, so the new token is accepted but not otherwise wired in —
no behavior change.

Verified: dotnet build Tap.slnx succeeds with zero warnings.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Commit 7d3604c moved the workspace path base from the .tap subdir to the
workspace root (so the AI feature can see the whole repo): WorkspaceLoader and
WorkspacePathResolver now root everything at the workspace dir, and every
workspace-relative path carries the ".tap/" segment. Auth/env endpoints already
pass full RelativePaths so they kept working, but two consumers still assumed
the old .tap-less convention and silently returned nothing:

Backend — CollectionEndpoints built "collections/<slug>/..." paths for
FindByPath / ReadSource / Save / TryResolve and the fallback dir scan, none of
which match the now ".tap/collections/..."-rooted model. Route every path
through CollectionDirRel/CollectionFileRel helpers that include the .tap segment,
and update SlugFromCollectionFile to parse the 4-part form.

Frontend — buildRequestsView looked for a top-level "collections" tree node, but
the tree now nests it under a ".tap" wrapper (".tap/collections"). Descend into
the wrapper (findCollectionsDir, tolerant of both shapes) and fix the display
prefix to ".tap/collections/".

Verified end-to-end against the sample workspace: GET /api/collections returns
all three collections, GET /api/collections/{slug} returns detail + source, and
the Studio sidebar renders the collection tree with nested folders and requests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Tap workspaces keep their content (tap.md, collections/, auth/, environments/)
at the workspace root; `.tap/` is reserved for settings. The whole codebase
already assumes this — WorkspaceLoader scans from the root (7d3604c),
EnsureWorkspaceScaffold writes tap.md at the root, and the create/import paths
(CreateNewDialog, RequestEndpoints, PostmanImporter) all write "collections/…"
without a `.tap/` prefix. The bundled sample was the lone holdout: it kept
everything under `.tap/`, which is what actually broke the collections view.

- Move samples/Studio.AppHost/sample-workspace → samples/sample-workspace and
  hoist .tap/{tap.md,collections,auth,environments} up to the workspace root.
- Point Studio.AppHost + .claude/launch.json at the new location.
- Revert 64d6513: with the sample fixed, CollectionEndpoints and the Sidebar
  request-tree go back to the plain "collections/" convention the rest of the
  code uses (that commit adapted the read side to the misplaced `.tap/` content,
  treating the symptom). Fix a stale `.tap/` path comment in PostmanImporter.

Verified against samples/sample-workspace: /api/workspace reports the new root
with zero load errors; /api/collections returns demo/github/streams; auths (13)
and environments (1) load; the tree is rooted at [auth, collections,
environments] (no `.tap` wrapper); and the Studio sidebar renders the collection
tree. Full solution build is clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
demo-oauth-pkce and demo-oauth-ropc baked the Aspire-allocated OpenIddict port
(localhost:61836) into authorizeUrl/tokenUrl, so the generated authorize URL
pointed at a stale port on the next run. Use the {{DEMO_API_URL}} token — the
same one already on the `authority` line and in demo-oauth-cc's tokenUrl — which
the workspace `env` provider resolves from the DEMO_API_URL env var the
Studio.AppHost injects (host:port of the demo-api endpoint, allowlisted via
TAP_VARS_ALLOWED).

Verified: workspace loads with zero errors and both files now carry the template.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add an env-selected backend to the Tauri shell: when STUDIO_DESKTOP_URL is set
it skips the bundled sidecar and points the webview straight at that URL;
otherwise it self-hosts the sidecar as before (packaged builds are unchanged).
The sidecar-spawn and external-attach paths are factored into spawn_sidecar()
and attach_external().

Studio.AppHost gains a RunDesktop switch: with RunDesktop=true it adds a
`studio-desktop` executable resource that runs `yarn --cwd src/desktop dev` with
STUDIO_DESKTOP_URL pointed at the studio-ui endpoint and WaitFor(studio-ui). So
`RunDesktop=true aspire run` brings up demo-api + studio-api + studio-ui + the
native window in one shot, with Vite hot reload and the shared Aspire backend —
no compile-server needed for this loop.

Verified: the Rust shell compiles and the AppHost builds. (Actually launching the
window needs a real `aspire run`, which opens a GUI.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Document the RunDesktop knob (default false) so it's discoverable. builder
.Configuration reads it from appsettings.json like any other config key;
override locally via user-secrets or a RunDesktop=true env var rather than
committing true, so a plain `aspire run` stays headless.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`RunDesktop=true aspire run` failed spawning `yarn dev` for studio-desktop:
Yarn's PnP `.pnp.cjs` had a global-cache path baked to `/Users/.yarn` (missing
the user segment), so it couldn't resolve @tauri-apps/cli. AddViteApp's
`.WithYarn()` avoids this, but a raw AddExecutable("yarn", …) hits it.

- Give src/desktop its own `.yarnrc.yml` with `nodeLinker: node-modules`, so it
  resolves the CLI from a local node_modules instead of a HOME-derived global
  PnP cache. Verified `yarn tauri --version` works even with a mangled HOME.
- Forward the real HOME to the studio-desktop executable so corepack (~/.cache),
  cargo (~/.cargo) and rustup (~/.rustup) resolve when `tauri dev` runs.
- Gitignore the ACL permission tomls build.rs regenerates each build.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A Tauri webview can't drive an OAuth sign-in via a window.open popup, so "Run
auth" just sat at "Waiting for sign-in…". Hand the authorize URL to the real
browser instead.

- Rust: add tauri-plugin-opener + an `open_external(url)` command (registered in
  the ACL via build.rs, granted `allow-open-external` in the capability).
- UI: openLoginUrl() invokes open_external in the desktop shell and falls back to
  window.open in a browser; both ResponsePanel and AuthEditor use it.

Completion needs no popup/postMessage — the panels already poll the auth flow, so
once the user signs in and the browser hits the callback (the localhost redirect
in Aspire dev, reachable from the system browser; tap-studio:// deep link in a
packaged build), the backend exchanges + stores the token and the poll picks it
up.

Verified: Rust shell compiles, ui-studio type-checks/builds. The OAuth round-trip
itself needs a real `aspire run` + browser sign-in to exercise.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Port mango's browser discovery so the OAuth "open sign-in" step can target a
specific browser and profile — handy for testing auth against different
accounts/tenants.

Backend (Tap.Studio):
- BrowserLauncher discovers Chrome/Edge/Firefox/Safari + their profiles (Chromium
  Local State, Firefox profiles.ini) and launches a URL with --profile-directory
  / -P. Runs on the host (desktop sidecar or local Aspire studio-api).
- GET /api/browsers (list) + POST /api/browsers/open (launch); DTOs registered in
  the StudioJson source-gen context.

UI (ui-studio):
- BrowserPicker (browser + profile Selects) + useBrowserLaunch() hook that
  persists the choice (localStorage). Wired into the "Sign in to continue" step
  in ResponsePanel and AuthEditor: a chosen browser opens server-side, "System
  default" keeps the existing open (window.open / desktop system browser).

Verified: GET /api/browsers returns this host's real browsers + Chrome profiles;
backend and ui-studio both build clean. The picker renders in the sign-in step
after Run auth (needs a real auth flow to see on screen).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- desktop.yml: add a Resolve-bundles step so Windows builds nsis only for
  pre-release tags (WiX MSI rejects SemVer pre-release like 0.5.0-beta.1).
- sample whoami-pkce: drop the stray /foo suffix so the request hits
  /demo/auth/whoami like its siblings (was a 404).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…eout

The Windows job hung ~70m on 'Build Studio UI' — that step's run: block executes
in PowerShell on windows-latest (bash on mac/linux), where corepack's download
prompt waits on stdin forever. Force shell: bash for the step, set
COREPACK_ENABLE_DOWNLOAD_PROMPT=0 globally, and add a 30-min job timeout so a
future hang fails fast instead of burning the 6h default.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Windows desktop build hung in 'Build Studio UI': vite 8's Rolldown bundler
panics in its pnp crate on Windows (mangles C:\ paths for PnP .zip cache entries,
e.g. @pierre/trees), the worker threads die, and the build waits on them forever.
Switch ui-studio to the node-modules linker so Rolldown resolves real paths and
never enters PnP — same fix already applied to src/desktop. yarn.lock only drops
two npm:* wildcard descriptors; no dependency change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Tauri v2's NSIS updater artifact is _x64-setup.exe + .exe.sig (uploaded by
tauri-action), there's no .nsis.zip. The 'Attach updater signatures' step and
publish-updater both looked for the nonexistent zip — the attach step failed the
Windows job even though all installers built. Skip the Windows attach (tauri-
action already uploads exe+sig) and glob the .exe.sig in publish-updater.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…untime

The bundled Tap.Studio sidecar is a .NET app; the notarization-required hardened
runtime blocked CoreCLR's JIT ('Failed to create CoreCLR, HRESULT: 0x80070008'),
so the packaged app hung forever at 'Spawning sidecar'. Ship an entitlements.plist
granting allow-jit / allow-unsigned-executable-memory / disable-library-validation
and point bundle.macOS.entitlements at it. Verified locally: re-signing the sidecar
with these + hardened runtime makes it start and emit the studio.ready handshake.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A Finder/Dock-launched app inherits only a minimal PATH (/usr/bin:/bin:...), so
the .NET sidecar couldn't find user CLIs — azure-cli auth failed with 'Could not
run az', and gh/tailscale/cloudflared would too. Resolve the real PATH from a
login+interactive shell and pass it to the sidecar. Verified with the CI-built,
notarized sidecar: azure-cli auth goes failed('Could not run az') → completed
when the enriched PATH is supplied. No-op on Windows and in Aspire dev (which
already inherits the terminal PATH).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant